﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using EzDesk.Core.Data;
using EzDesk.Core.Model;
using EzDesk.Utility;
using EzDesk.Utility.EzML;
using EzDesk.Utility.Json;
using EzDesk.Core.Interface;
using EzDesk.Utility.Log;
using EzDesk.Utility.Web.Extensions;

namespace EzDesk.Core.ServiceImpl.Services
{
    public partial class CoreService : ICoreService
    {
        #region [ Send Feeds Methods ]

        private void addUserToFeed(Guid userId, Guid feedId, int feedTypeValue, string feedDataEx, DateTime feedCreateOn, EzDeskDatabaseContainer container)
        {
            container.Feed_RecipientSet.AddObject(
                new Feed_Recipient()
                {
                    Id = Guid.NewGuid(),
                    RecipientId = userId,
                    FeedId = feedId,
                    FeedDataEx = feedDataEx,
                    IsRead = false,
                    IsAvailable = true,
                    IsMark = false,
                    LastChangeOn = feedCreateOn,
                    FeedTypeValue = feedTypeValue,
                });
        }

        private string getPersonalFeedData(Guid userId, Guid feedId)
        {
            return EzDeskDatabaseContainer.Go(
                container =>
                {
                    return container.Feed_RecipientSet
                        .Where(p => p.RecipientId == userId && p.FeedId == feedId)
                        .Select(p => p.FeedDataEx)
                        .SingleOrDefault();
                });
        }

        public IList<Guid> getFeedRecord(Guid identity, string templateName, Guid? userId)
        {
            return EzDeskDatabaseContainer.Go(
                container =>
                {
                    var query = container.Feed_RecordSet
                        .Where(p => p.TemplateName == templateName && p.Identity == identity);
                    if (userId.HasValue)
                    {
                        query = query.Where(p => p.UserId == userId.Value);
                    }
                    return query.Select(p => p.FeedId).Distinct().ToList();
                });
        }
        
        private Guid innerSendFeed(FeedTemplate template, string feedData, string memberString, Guid? senderId, string ignoreUsers, bool isAvailable)
        {
            return EzDeskDatabaseContainer.GoAndSave(
                container =>
                    {
                        var templateId = template.Id;
                        var feedType = template.DefaultTypeValue;

                        // 验证FeedData和模板的匹配
                        try
                        {
                            validateFeedData(template, feedData);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("FeedData Error!", ex);
                        }


                        //处理ez标记
                        feedData = replaceEzTarget(feedData);
                        // 处理掉feedData中的html, 只留下空格
                        feedData = feedData.RemoveHtml(false);
                        //恢复ez标记
                        feedData = recoveryEzTarget(feedData);

                        Guid feedId = Guid.NewGuid();
                        DateTime now = DateTime.Now;

                        // 创建 Feed
                        Feed feed = new Feed()
                        {
                            Id = feedId,
                            TemplateId = templateId,
                            CreateOn = now,
                            UpdateOn = now,
                            LastChangeOn = now,
                            ExpireOn = now.AddDays(2), /* todo: update 过期时间 */
                            FeedData = feedData,
                            IsRead = false,
                            IsAvailable = isAvailable,
                            OwnerId = senderId,
                            Icon = template.TemplateIconName,
                        };
                        container.FeedSet.AddObject(feed);
                        LogEx.TRACE("Core - SendFeed", "新Feed写入数据库成功 " + feed.Id);

                        // 把Feed发送给的人
                        IList<Guid> usersIdsToSend = getAllUserIdsByMemberString(memberString);

                        var allUserIds = getAllUserIdsByMemberString(memberString);
                        var ignoreUserIds = getAllUserIdsByMemberString(ignoreUsers);

                        allUserIds
                            .Where(p => !ignoreUserIds.Contains(p))
                            .Foreach(userId =>
                                addUserToFeed(userId, feedId, feedType, null, now,container)
                                );
                        
                        return feedId;
                    });
            
        }


        Guid ICoreService.SendFeed(
            string templateName, string feedData, string memberString, DateTime expireOn,
            Guid? senderId, string ignoreUsers, bool isAvailable
            )
        {
            return EzDeskDatabaseContainer.Go(
                container =>
                    {
                        // 消息模板
                        var template = container.FeedTemplateSet.FirstOrDefault(p => p.TemplateName == templateName);
                        if (template == null)
                        {
                            throw new Exception("Feed Template not Found!");
                        }
                        return innerSendFeed(template, feedData, memberString, senderId, ignoreUsers, isAvailable);
                    });
        }




        private void validateFeedData(FeedTemplate template, string feedData)
        {
            EzMLHelper.MergeDataToEzML(template.Title, feedData);
            EzMLHelper.MergeDataToEzML(template.Body, feedData);
        }


        void ICoreService.UpdatePersonalFeedData(Guid feedId, string memberstring, string feedDataEx)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                    {
                        var userIds = self.GetAllUserIdsByMemberString(memberstring);
                        foreach (var userId in userIds)
                        {
                            // 合并FeedData
                            string originalData = getPersonalFeedData(userId, feedId);
                            var feedDataToUpdate = merageExtraFeedData(originalData, feedDataEx);
                            if (feedDataToUpdate == originalData) continue;

                            var item = container.Feed_RecipientSet
                                .Where(p => p.RecipientId == userId && p.FeedId == feedId)
                                .SingleOrDefault();
                            if (item != null)
                            {
                                item.FeedDataEx = feedDataEx;
                                item.LastChangeOn = DateTime.Now;
                            }
                            // 更新数据后，设为未读
                            setFeedIsRead(feedId, userId.ToString(), false);
                        }
                    });
            
        }



        void ICoreService.UpdatePersonalFeedType(Guid feedId, string memberstring, string feedTypeStr)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                    {
                        var feedTypeValue = FeedTypeDictionary.StringToIntType(feedTypeStr);
                        var userIds = self.GetAllUserIdsByMemberString(memberstring);
                        foreach (var userId in userIds)
                        {
                            var item = container.Feed_RecipientSet
                                          .Where(p => p.RecipientId == userId && p.FeedId == feedId)
                                          .SingleOrDefault();
                            if (item != null)
                            {
                                item.FeedTypeValue = feedTypeValue;
                                item.LastChangeOn = DateTime.Now;
                            }
                            // 更新数据后，设为未读
                            setFeedIsRead(feedId, userId.ToString(), false);
                        }
                    });
        }

        #region [ private methods - Set Feed Properties Methods]

        void updateFeedData(Guid[] feedIds, string newFeedData)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                {
                    foreach (var feedId in feedIds)
                    {
                        var id = feedId;
                        var feed = container.FeedSet.FirstOrDefault(p => p.Id == id);

                        var feedTemplate =
                            container.FeedTemplateSet.FirstOrDefault(p => p.Id == feed.TemplateId);

                        string newFeedDataToUpdate = merageNewFeedData(feed.FeedData, newFeedData);
                        validateFeedData(feedTemplate, newFeedDataToUpdate);
                        feed.FeedData = newFeedDataToUpdate;
                        feed.UpdateOn = DateTime.Now;
                        // 更新了数据 设为未读
                        feed.IsRead = false;
                    }
                });
        }

        void setFeedIsRead(Guid feedId, string memberString, bool isRead)
        {
            setFeedIsRead(
                convertSingleIdToIdCollection(feedId),
                memberString,
                isRead);
        }
        void setFeedIsRead(string feedIdStrs, string memberString, bool isRead)
        {
            setFeedIsRead(
                convertIdStrsToIdCollection(feedIdStrs),
                memberString,
                isRead
                );
        }

        void setFeedIsRead(Guid[] feedIds, string memberString, bool isRead)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                {
                    var userIds = self.GetAllUserIdsByMemberString(memberString);
                    foreach (var feedId in feedIds)
                    {
                        foreach (var userId in userIds)
                        {
                            var feedRecipient =
                                      container.Feed_RecipientSet.FirstOrDefault(
                                          p => p.FeedId == feedId && p.RecipientId == userId);
                            if (feedRecipient == null) continue;
                            feedRecipient.IsRead = isRead;
                            feedRecipient.LastChangeOn = DateTime.Now;
                        }
                    }
                });
        }


        void setFeedIsAvailable(Guid feedId, string memberString, bool isAvailable)
        {
            setFeedIsAvailable(
                convertSingleIdToIdCollection(feedId),
                memberString,
                isAvailable
                );
        }


        void setFeedIsAvailable(string feedIdStrs, string memberString, bool isAvailable)
        {
            setFeedIsAvailable(
                convertIdStrsToIdCollection(feedIdStrs),
                memberString,
                isAvailable
                );
        }

        void setFeedIsAvailable(Guid[] feedIds, string memberString, bool isAvailable)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                {
                    var userIds = self.GetAllUserIdsByMemberString(memberString);
                    foreach (var feedId in feedIds)
                    {
                        foreach (var userId in userIds)
                        {
                            var feedRecipient =
                                      container.Feed_RecipientSet.FirstOrDefault(
                                          p => p.FeedId == feedId && p.RecipientId == userId);
                            if (feedRecipient == null) continue;
                            feedRecipient.IsAvailable = isAvailable;
                            feedRecipient.LastChangeOn = DateTime.Now;
                        }
                    }
                });
        }

        void setFeedIsMark(string feedIdStrs, string memberString, bool isMark)
        {
            setFeedIsMark(
                convertIdStrsToIdCollection(feedIdStrs),
                memberString,
                isMark
                );
        }

        void setFeedIsMark(Guid feedId, string memberString, bool isMark)
        {
            setFeedIsMark(
                convertSingleIdToIdCollection(feedId),
                memberString,
                isMark);
        }

        void setFeedIsMark(Guid[] feedIds, string memberString, bool isMark)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                {
                    var userIds = self.GetAllUserIdsByMemberString(memberString);
                    foreach (var feedId in feedIds)
                    {
                        foreach (var userId in userIds)
                        {
                            var feedRecipient =
                                      container.Feed_RecipientSet.FirstOrDefault(
                                          p => p.FeedId == feedId && p.RecipientId == userId);
                            if (feedRecipient == null) continue;
                            feedRecipient.IsMark = isMark;
                            feedRecipient.LastChangeOn = DateTime.Now;
                        }
                    }
                });
        }

        private string replaceEzTarget(string str)
        {
            var strReplaced = Regex.Replace(str, @"</ez:(?<content>.*?)>", @"[/ez:${content}]", RegexOptions.IgnoreCase);
            strReplaced = Regex.Replace(strReplaced, @"<ez:(?<content>.*?)>", @"[ez:${content}]", RegexOptions.IgnoreCase);

            return strReplaced;
        }

        private string recoveryEzTarget(string str)
        {
            var strReplaced = Regex.Replace(str, @"\[/ez:(?<content>.*?)\]", @"</ez:${content}>", RegexOptions.IgnoreCase);
            strReplaced = Regex.Replace(strReplaced, @"\[ez:(?<content>.*?)\]", @"<ez:${content}>", RegexOptions.IgnoreCase);

            return strReplaced;
        }

        #endregion
        #endregion

        #region [ Update Feeds Methods]

        void ICoreService.UpdateFeedData(Guid feedId, string newFeedData)
        {
            updateFeedData(
                convertSingleIdToIdCollection(feedId),
                newFeedData
                );
        }

        void ICoreService.UpdateFeedData(string feedIdStrs, string newFeedData)
        {
            updateFeedData(
                convertIdStrsToIdCollection(feedIdStrs),
                newFeedData
                );
        }

        void ICoreService.UpdateFeedStatus(IList<FeedStatus> feedStatus, string memberString)
        {
            foreach (var feed in feedStatus)
            {
                try
                {
                    if (feed.IsRead.HasValue) setFeedIsRead(feed.FeedId, memberString, feed.IsRead.Value);
                    if (feed.IsAvailable.HasValue) setFeedIsAvailable(feed.FeedId, memberString, feed.IsAvailable.Value);
                    if (feed.IsMark.HasValue) setFeedIsMark(feed.FeedId, memberString, feed.IsMark.Value);
                }
                catch (Exception ex)
                {
                    LogEx.ERROR("Core - FeedUpdate", "更新Feed时失败", ex);
                    continue;
                }
            }
        }

        private string merageNewFeedData(string originalFeedData, string newFeedData)
        {
            IDictionary<string, string> orignalData = JsonHelper.JsonToDictionary(originalFeedData);
            IDictionary<string, string> newData = JsonHelper.JsonToDictionary(newFeedData);
            // 更新 原有字段信息
            foreach (var item in newData)
            {
                if (orignalData.ContainsKey(item.Key))
                {
                    orignalData[item.Key] = item.Value;
                }
                else
                {
                    //todo: error
                    throw new Exception("Feed Data Error! " + item.Key + " not found!");
                }
            }
            return JsonHelper.ConvertToJson(orignalData);
        }

        private string merageExtraFeedData(string originalFeedData, string newFeedData)
        {
            IDictionary<string, string> orignalData = JsonHelper.JsonToDictionary(originalFeedData);
            IDictionary<string, string> newData = JsonHelper.JsonToDictionary(newFeedData);

            foreach (var item in newData)
            {
                if (orignalData.ContainsKey(item.Key))
                {
                    orignalData[item.Key] = item.Value;
                }
                else
                {
                    orignalData.Add(item.Key, item.Value);
                }
            }
            return JsonHelper.ConvertToJson(orignalData);
        }
        #endregion

        #region [ Delete Methods ]
        void ICoreService.DeleteFeed(Guid feedId, int type)
        {
            throw  new NotImplementedException();
        }
        #endregion


        #region [ Get Feeds ]

        IList<Feed> ICoreService.GetFeeds(string feedTypeStr, Guid userId, DateTime from, DateTime to, int maxCount, bool? isRead, bool? isAvailable, bool? isMark)
        {
            return EzDeskDatabaseContainer.Go(
                container =>
                    {
                        int? feedType = FeedTypeDictionary.StringToIntType(feedTypeStr);

                        if (feedType == -1)
                        {
                            feedType = null;
                        }

                        var feedRecipients =
                            container.Feed_RecipientSet.Where(p => p.RecipientId == userId && p.LastChangeOn > from && p.LastChangeOn < to);
                        if (feedType.HasValue)
                        {
                            feedRecipients = feedRecipients.Where(p => p.FeedTypeValue == feedType.Value);
                        }
                        if (isRead.HasValue)
                        {
                            feedRecipients = feedRecipients.Where(p => p.IsRead == isRead.Value);
                        }
                        if (isAvailable.HasValue)
                        {
                            feedRecipients = feedRecipients.Where(p => p.IsAvailable == isAvailable.Value);
                        }
                        if (isMark.HasValue)
                        {
                            feedRecipients = feedRecipients.Where(p => p.IsMark == isMark.Value);
                        }
                      
                        var feedsList1 = feedRecipients.ToList().ExtendFeedModel(container);
                        
                        var feedsList2 = container.FeedSet
                                .Where(p => p.UpdateOn > from && p.UpdateOn < to)
                                .ToList()
                                .ExtendFeedModelForUser(userId, feedType, isRead, isAvailable, isMark, container);


                        return feedsList1.Union(feedsList2)
                            .OrderByDescending(p => p.UpdateOn > p.LastChangeOn ? p.UpdateOn : p.LastChangeOn)
                            .Take(maxCount)
                            .ToList();
                    });
        }

        IList<Feed> ICoreService.GetFeeds(string feedTypeStr, Guid userId, int startIndex, int count, bool? isRead, bool? isAvaiable, bool? isMark)
        {

            // todo: 看来得给Feed添加一个字段 用来Get的时候，否则每次既要判断Feed_Recipient的LastChangeOn又要判断Feed的UpdateOn，程序复杂而且影响性能
            return EzDeskDatabaseContainer.Go(
                container =>
                    {
                        
                        int? feedType = FeedTypeDictionary.StringToIntType(feedTypeStr);

                        // 如果 feedTypeStr 是null 或者非法的东西，就当它不存在，返回所有类型
                        if (feedType == -1)
                        {
                            feedType = null;
                        }

                        IQueryable<Feed> feeds = container.FeedSet
                            .OrderByDescending(p => p.UpdateOn);


                        return feeds
                            .ToList()
                            .ExtendFeedModelForUser(userId, feedType, startIndex, count, isRead, isAvaiable,
                                                isMark, container);
                    });
        }

        #endregion

        string ICoreService.GetPersonalFeedData(Guid userId, Guid feedId)
        {
            return getPersonalFeedData(userId, feedId);
        }


        #region [ Feed Records ]
        public void SetFeedRecord(Guid identity, string templateName, string memberstring, Guid feedId, string ignoreUsers)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                    {
                        var allUserIds = getAllUserIdsByMemberString(memberstring);
                        var ignoreUserIds = getAllUserIdsByMemberString(ignoreUsers);

                        allUserIds
                            .Where(p => !ignoreUserIds.Contains(p))
                            .Foreach(userId =>
                                     container.Feed_RecordSet.AddObject(
                                         new Feed_Record
                                             {
                                                 FeedId = feedId,
                                                 TemplateName = templateName,
                                                 Identity = identity,
                                                 UserId = userId
                                             }
                                         ));
                    });
        }

        public string GetFeedRecord(Guid identity, string templateName, string userIds)
        {
            var allUserIds = getAllUserIdsByMemberString(userIds);
            IList<Guid> feedIdList = new List<Guid>();

            if (allUserIds.Count == 0)
            {
                feedIdList = getFeedRecord(identity, templateName, null);
            }
            else
            {
                IEnumerable<Guid> feedList = feedIdList;
                allUserIds.Foreach(
                    userId =>
                    {
                        feedList = getFeedRecord(identity, templateName, userId);
                    });
                feedIdList = feedList.ToList();
            }
            return feedIdList.Join(",");

        }

        

        #endregion


    }
}
